﻿//////////////////////////////////////////////////////////////////
//
// Program.cs
//
// Copyright (c) 2012 Dan Pike.
//
// Permission is hereby granted, free of charge, to any person
// obtaining a copy of this software and associated documentation files
// (the "Software"), to deal in the Software without restriction,
// including without limitation the rights to use, copy, modify, merge,
// publish, distribute, sublicense, and/or sell copies of the Software,
// and to permit persons to whom the Software is furnished to do so,
// subject to the following conditions:
//
// The above copyright notice and this permission notice shall be included
// in all copies or substantial portions of the Software.
//
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
// OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
// MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
// IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY
// CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,
// TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE
// SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
//
using SharpDX;
using SharpDX.D3DCompiler;
using SharpDX.Direct3D;
using SharpDX.Direct3D11;
using SharpDX.DXGI;
using SharpDX.Windows;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;

using Buffer = SharpDX.Direct3D11.Buffer;
using Device = SharpDX.Direct3D11.Device;
using System.Drawing;
using Geometry;

namespace LucyDirect
{
   class Program
   {
      static void Main(string[] args)
      {
         RenderForm form = new RenderForm("Lucy DirectX version");

         // SwapChain description
         SwapChainDescription desc = new SwapChainDescription()
         {
            BufferCount = 1,
            ModeDescription = new ModeDescription(form.ClientSize.Width, form.ClientSize.Height,
            new Rational(60, 1), Format.R8G8B8A8_UNorm),
            IsWindowed = true,
            OutputHandle = form.Handle,
            SampleDescription = new SampleDescription(1, 0),
            SwapEffect = SwapEffect.Discard,
            Usage = Usage.RenderTargetOutput
         };

         // Create Device and SwapChain
         Device device;
         SwapChain swapChain;
         Device.CreateWithSwapChain(DriverType.Hardware, DeviceCreationFlags.None, desc, out device, out swapChain);
         DeviceContext context = device.ImmediateContext;

         // New RenderTargetView from the back-buffer
         Texture2D backBuffer = Texture2D.FromSwapChain<Texture2D>(swapChain, 0);
         RenderTargetView renderView = new RenderTargetView(device, backBuffer);

         ShaderBytecode shaderByteCode = ShaderBytecode.CompileFromFile("LucySharpDX.fx", "fx_5_0", ShaderFlags.None, EffectFlags.None);
         Effect effect = new Effect(device, shaderByteCode);
         EffectTechnique technique = effect.GetTechniqueByIndex(0);
         EffectPass pass = technique.GetPassByIndex(0);

         // Layout from VertexShader input signature
         ShaderBytecode passSignature = pass.Description.Signature;
         InputLayout layout = new InputLayout(device, passSignature, new[]
         {
            new InputElement("POSITION", 0, Format.R32G32B32A32_Float, 0, 0),
            new InputElement("COLOR", 0, Format.R32G32B32A32_Float, 16, 0)
         });

         Icosahedron.clockwise = true; // for Direct3D
         Icosahedron icos = new Icosahedron(1.0f);
         List<Triangle> triangles = icos.triangles.ToList();                  // 20 triangles
         triangles = triangles.SelectMany(triangle => triangle.into4).ToList();        // 80
         triangles = triangles.SelectMany(triangle => triangle.into4).ToList();        // 320
         triangles = triangles.SelectMany(triangle => triangle.into4).ToList();        // 1280
         triangles = triangles.SelectMany(triangle => triangle.into4).ToList();        // 5120
         triangles = triangles.SelectMany(triangle => triangle.into4).ToList();        // 20480

         //triangles.Clear();
         //triangles.Add(new Triangle(new Point3D(-0.5f, -0.5f, 0.5f), new Point3D(0.0f, 0.5f, 0.5f), new Point3D(0.5f, -0.5f, 0.5f)));

         List<Vector4> vectors = new List<Vector4>();
         triangles.ForEach(triangle => triangle.vertices.ToList().ForEach(vertex =>
         {
            // Project the point out to a unit sphere
            float scale = 1.0f / (float)Math.Sqrt(vertex.x * vertex.x + vertex.y * vertex.y + vertex.z * vertex.z);
            vertex = vertex.Mul(scale);

            float radius = 0.4f;
            vertex = vertex.Mul(radius);
            vectors.Add(new Vector4(vertex.x, vertex.y, vertex.z, 1.0f));

            // Vertices vary from -1.0 to +1.0, we want colours to vary from 0 to 1.0
            Geometry.Point3D colors = vertex.Add(1.0f, 1.0f, 0.0f).Mul(0.5f);
            vectors.Add(new Vector4(colors.x, colors.y, colors.z, 1.0f));
         }));

         Buffer vertices = Buffer.Create(device, BindFlags.VertexBuffer, vectors.ToArray());

         // Prepare All the stages
         context.InputAssembler.InputLayout = layout;
         context.InputAssembler.PrimitiveTopology = PrimitiveTopology.TriangleList;
         context.InputAssembler.SetVertexBuffers(0, new VertexBufferBinding(vertices, 32, 0));
         context.Rasterizer.SetViewports(new Viewport(0, 0, form.ClientSize.Width, form.ClientSize.Height, 0.0f, 1.0f));
         context.OutputMerger.SetTargets(renderView);

         RenderLoop.Run(form, () =>
            {
               context.ClearRenderTargetView(renderView, SystemColors.ButtonFace);
               for (int i = 0; i < technique.Description.PassCount; ++i)
               {
                  pass.Apply(context);
                  context.Draw(3 * triangles.Count, 0);
               }
               swapChain.Present(0, PresentFlags.None);
            });

         // Release all resources
         passSignature.Dispose();
         effect.Dispose();
         shaderByteCode.Dispose();
         vertices.Dispose();
         layout.Dispose();
         renderView.Dispose();
         backBuffer.Dispose();
         device.Dispose();
         swapChain.Dispose();
         //factory.Dispose();
      }
   }
}
